using System;
using System.Runtime.Serialization;
using System.Security.Permissions;
using InfiniTec.Exchange.Notifications.Microsoft.Exchange.WebServices;

namespace InfiniTec.Exchange.Notifications
{
	/// <summary>
	/// Encapsulates a reference to an item and a changekey to an item on an Exchange 2007 public folder or mailbox folder.
	/// </summary>
	[Serializable]
	public sealed class ItemReference: ISerializable, IEquatable<ItemReference>
	{
		/// <summary>
		/// Gets or sets the id of the referenced item.
		/// </summary>
		/// <value>The id.</value>
		public string Id { get; private set; }
		/// <summary>
		/// Gets or sets the change key of the referenced item.
		/// </summary>
		/// <value>The change key.</value>
		public string ChangeKey { get; private set; }

		/// <summary>
		/// Initializes a new instance of the <see cref="ItemReference"/> class.
		/// </summary>
		/// <param name="id">The ItemId.</param>
		/// <param name="changeKey">The ChangeKey of the item.</param>
		public ItemReference(string id, string changeKey)
		{
			if (string.IsNullOrEmpty(id)) throw new ArgumentNullException("id");
			Id = id;
			ChangeKey = changeKey;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ItemReference"/> class.
		/// </summary>
		/// <param name="id">The ItemId.</param>
		public ItemReference(string id): this(id, null)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ItemReference"/> class.
		/// </summary>
		/// <param name="itemId">The item id.</param>
		internal ItemReference(ItemIdType itemId)
		{
			if (itemId == null) throw new ArgumentNullException("itemId");
			Id = itemId.Id;
			ChangeKey = itemId.ChangeKey;
		}

		/// <summary>
		/// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
		/// </summary>
		/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
		/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
		/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
		void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
		{
			info.AddValue("Id", Id);
			info.AddValue("ChangeKey", ChangeKey);
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ItemReference"/> class.
		/// </summary>
		/// <param name="info">The info.</param>
		/// <param name="context">The context.</param>
		private ItemReference(SerializationInfo info, StreamingContext context)
		{
			Id = info.GetString("Id");
			ChangeKey = info.GetString("ChangeKey");
		}

		/// <summary>
		/// Indicates whether the current object is equal to another object of the same type.
		/// </summary>
		/// <returns>
		/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
		/// </returns>
		/// <param name="other">An object to compare with this object.
		///                 </param>
		public bool Equals(ItemReference other)
		{
			if (ReferenceEquals(null, other)) return false;
			if (ReferenceEquals(this, other)) return true;
			return Equals(other.Id, Id);
		}

		/// <summary>
		/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
		/// </summary>
		/// <returns>
		/// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
		/// </returns>
		/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. 
		///                 </param><exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.
		///                 </exception><filterpriority>2</filterpriority>
		public override bool Equals(object obj)
		{
			if (ReferenceEquals(null, obj)) return false;
			if (ReferenceEquals(this, obj)) return true;
			if (obj.GetType() != typeof (ItemReference)) return false;
			return Equals((ItemReference) obj);
		}

		/// <summary>
		/// Serves as a hash function for a particular type. 
		/// </summary>
		/// <returns>
		/// A hash code for the current <see cref="T:System.Object"/>.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		public override int GetHashCode()
		{
			return Id.GetHashCode();
		}

		public static bool operator ==(ItemReference left, ItemReference right)
		{
			return Equals(left, right);
		}

		public static bool operator !=(ItemReference left, ItemReference right)
		{
			return !Equals(left, right);
		}
	}
}